/*
 * test.cpp
 *
 *  Created on: Mar 28, 2012
 *      Author: Stephen
 */

#include <iostream>
#include <cstdio>
#include <string>
#include <AL/al.h>
#include <AL/alc.h>
#include <mpg123.h>

ALCdevice *device;
ALCcontext *context;
mpg123_handle *m;
unsigned int size,done;
int err,channels = 0, encoding = 0;
long rate = 0;
unsigned char *mpgbuffer;
unsigned int albuffer[1];
unsigned int source[1];

void cleanup(mpg123_handle *x)
{

	mpg123_close(x);
	mpg123_delete(x);
	mpg123_exit();
}

void cleanupAl()
{
	alcMakeContextCurrent(NULL);
	alcDestroyContext(context);
	alDeleteBuffers(1, albuffer);
	alDeleteSources(1, source);
}

void initloadmpg123()
{

	//INIT MPG123
	err = mpg123_init();
	if(err != MPG123_OK || (m = mpg123_new(NULL, &err)) == NULL)
	{
		fprintf(stderr, "ERROR initialization");
		cleanup(m);
		exit(0);
	}

	//LOAD MP3
	if(mpg123_open(m, "PR.mp3") != MPG123_OK || mpg123_getformat(m, &rate, &channels, &encoding) != MPG123_OK)
	{
		fprintf(stderr, "ERROR loading mp3");
		cleanup(m);
		exit(0);
	}
}

void initDevCont()
{
	//INIT OPENAL
	//DEVICE
	if((device = alcOpenDevice("Generic Hardware")) == NULL)
	{
		fprintf(stderr, "ERROR loading device");
		cleanup(m);
		exit(0);
	}

	//CONTEXT
	context = alcCreateContext(device, NULL);
	err = alcGetError(device);
	if(err == ALC_INVALID_VALUE || err == ALC_INVALID_DEVICE)
	{
		fprintf(stderr, "ERROR loading context");
		alcCloseDevice(device);
		cleanup(m);
		exit(0);
	}
	alcMakeContextCurrent(context);
}

void decToStream()
{
	//DECODING
	size = mpg123_length(m) * channels * sizeof(int16_t);
	done = 0;
	mpgbuffer = (unsigned char*)malloc(size);

	//cleanup(m);
	//initloadmpg123();

	//err = mpg123_decode(m, mpin, fsize, mpgbuffer, size, &done);
	err = mpg123_read(m, mpgbuffer, size, &done);
	if(err == MPG123_OK)
	{
		printf("MPG OK\n");
	}
	else if(err == MPG123_DONE)
	{
		printf("MPG DONE\n");
	}
	cleanup(m);
}

void streamToBuffer()
{
	alGenBuffers(1, albuffer);
	if((err = alGetError()) != AL_NO_ERROR)
	{
		fprintf(stderr, "ERROR buffer failure error code: %d", err);
		alcMakeContextCurrent(NULL);
		alcDestroyContext(context);
		cleanup(m);
		exit(0);
	}

	alBufferData(albuffer[0], AL_FORMAT_STEREO16, mpgbuffer, done, int(rate));
}

void initSourceListener()
{
	//SOURCE
	alGenSources(1,source);

	err = alGetError();
	if(err != AL_NO_ERROR)
	{
		fprintf(stderr, "WARNING error detected after alGenSources code: %d",err);
		cleanup(m);
		cleanupAl();
		exit(0);
	}
	//alSourceQueueBuffers(source[0], 1, albuffer);
	alSourcei(source[0],AL_BUFFER,albuffer[0]);
	alSourcef (source[0], AL_PITCH,    1.0f     );
    alSourcef (source[0], AL_GAIN,     1.0f     );
	alSource3f(source[0], AL_POSITION, 0,0,0);
	alSource3f(source[0], AL_VELOCITY, 0,0,0);
	alSource3f(source[0], AL_DIRECTION, 0,0,0);
	alSourcei(source[0], AL_LOOPING, 0);

	//LISTENER

	alListener3f(AL_POSITION,0,0,0);
	alListener3f(AL_VELOCITY,0,0,0);
	float stuff[3] = {0,0,0};
	alListenerfv(AL_ORIENTATION, stuff);
}

int main()
{
	initloadmpg123();
	printf("Rate: %ld Channels: %d Encoding: %d\n", rate,channels,encoding);
	initDevCont();

	decToStream();

	//EXTENSION, BUFFERS
	/*
	bool exten;
	exten = alIsExtensionPresent("EAX2.0");
	*/

	alGetError(); //CLEAR ERROR CODE

	streamToBuffer();
	//cleanup(m);

	initSourceListener();

	alSourcePlay(source[0]);
	//std::cout << "output to quit\n" << std::endl;
	//std::string bah;
	//std::cin >> bah;
	int bah;
	alGetSourcei(source[0], AL_BUFFERS_PROCESSED, &bah);
	while(bah == 0){
		alGetSourcei(source[0], AL_BUFFERS_PROCESSED, &bah);
	}
	alSourceStop(source[0]);
	alcMakeContextCurrent(NULL);
	alcDestroyContext(context);
	alDeleteBuffers(1, albuffer);
	alDeleteSources(1,source);
	fprintf(stderr,"FIN");
	return 0;
}
